﻿using System;
using ArtMath.PointCollection;
using ArtMath.Component.Point;
using ArtMath.Core.Data;
using ArtMath.Attribute;
using System.ComponentModel;
using ArtMath.Converter;
using System.Linq;
using System.Drawing;
using ArtMath.Core.Helper;
using ArtMath.Helper;
using ArtMath.Resource;
using ArtMath.Flag;
using ArtMath.Data;
using ArtMath.Setting;
using ArtMath.CommonType;

namespace ArtMath.Component.Circle
{
    /// <summary>
    /// 半径圆心角扇形
    /// </summary>
    [Serializable]
    public class RadiusCentralAngleSector : PartialCircleBase
    {
        AngleStringX ctAngle;
        public RadiusCentralAngleSector(SketchPad pad, string name, string angle, bool addToList = true) : base(pad, name, ToolTypeName.bjyxjsx, addToList)
        {
            pske.Curves = new Curve[] { new Curve() };
            InPoints = new PointBase[2];
            StrCentralAngle = new AngleStringX(Pad, angle);
        }
        public override PointCollectionsManager Skeleton
        {
            get
            {
                PointCollectionsManager res = base.Skeleton;
                LineSegment ls1 = new LineSegment(Center, Point1.ToPointD()),
                    ls2 = new LineSegment(Point2, Center);
                res.AddPointCollection(ls1);
                res.AddPointCollection(ls2);
                res.ConnectHeadAndTail(ls1, ske);
                res.ConnectHeadAndTail(ske, ls2);
                res.ConnectHeadAndTail(ls2, ls1);
                return res;
            }
        }
        public override PointCollectionsManager PSkeleton
        {
            get
            {
                PointCollectionsManager res = base.PSkeleton;
                LineSegment ls1 = new LineSegment(PCenter, Point1.Pxy),
                    ls2 = new LineSegment(Pad.XYToPxy(Point2), PCenter);
                res.AddPointCollection(ls1);
                res.AddPointCollection(ls2);
                res.ConnectHeadAndTail(ls1, pske);
                res.ConnectHeadAndTail(pske, ls2);
                res.ConnectHeadAndTail(ls2, ls1);
                return res;
            }
        }
        public override PointCollectionsManager NewSkeleton
        {
            get
            {
                PointCollectionsManager res = base.NewSkeleton;
                PointCollectionBase tmp = res.PointCollections.Single();
                LineSegment ls1 = new LineSegment(NewCenter, Point1.NewXY),
                    ls2 = new LineSegment(NewPoint2, NewCenter);
                res.AddPointCollection(ls1);
                res.AddPointCollection(ls2);
                res.ConnectHeadAndTail(ls1, tmp);
                res.ConnectHeadAndTail(tmp, ls2);
                res.ConnectHeadAndTail(ls2, ls1);
                return res;
            }
        }
        public override PointCollectionsManager NewPSkeleton
        {
            get
            {
                PointCollectionsManager res = base.NewPSkeleton;
                PointCollectionBase tmp = res.PointCollections.Single();
                LineSegment ls1 = new LineSegment(NewPCenter, Point1.NewPxy),
                    ls2 = new LineSegment(Pad.XYToPxy(NewPoint2), NewPCenter);
                res.AddPointCollection(ls1);
                res.AddPointCollection(ls2);
                res.ConnectHeadAndTail(ls1, tmp);
                res.ConnectHeadAndTail(tmp, ls2);
                res.ConnectHeadAndTail(ls2, ls1);
                return res;
            }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("CentralAngle")]
        public AngleStringX StrCentralAngle
        {
            get { return ctAngle; }
            set
            {
                if (ctAngle is object)
                {
                    ctAngle.ExpressionSetting -= CentralAngle_ExpressionSetting;
                    ctAngle.ExpressionSet -= CentralAngle_ExpressionSet;
                    ctAngle.ParametersValueChanging -= CentralAngle_ParametersValueChanging;
                    ctAngle.ParametersValueChanged -= CentralAngle_ParametersValueChanged;
                }
                ctAngle = value;
                if (ctAngle is object)
                {
                    ctAngle.ExpressionSetting += CentralAngle_ExpressionSetting;
                    ctAngle.ExpressionSet += CentralAngle_ExpressionSet;
                    ctAngle.ParametersValueChanging += CentralAngle_ParametersValueChanging;
                    ctAngle.ParametersValueChanged += CentralAngle_ParametersValueChanged;
                }
            }
        }

        void CentralAngle_ParametersValueChanging(DocStringX sender)
        {
            CenterPoint.TempMoveLock = Point1.TempMoveLock = true;
            Pad.MoveObject_DoMouseDown(this);
            CenterPoint.TempMoveLock = Point1.TempMoveLock = false;
        }

        void CentralAngle_ExpressionSetting(StringX sender)
        {
            CenterPoint.TempMoveLock = Point1.TempMoveLock = true;
            Pad.MoveObject_DoMouseDown(this);
            CenterPoint.TempMoveLock = Point1.TempMoveLock = false;
        }

        void CentralAngle_ExpressionSet(StringX sender, ExpressionSetEventArgs e)
        {
            CenterPoint.TempMoveLock = Point1.TempMoveLock = true;
            if (Pad.MoveObject_DoMouseMove(this, PointD.Empty))
            {
                ComponentChanged(this);
            }
            else
            {
                Pad.ShowMessage(string.Format(Resources.Translate("FailModify"), OfTranslator("CentralAngle")), MessageType.Error);
                StrCentralAngle.SetExpression(e.OldExpression, false);
            }
            Pad.MoveObject_DoMouseUp(this);
            CenterPoint.TempMoveLock = Point1.TempMoveLock = false;
        }
        void CentralAngle_ParametersValueChanged(DocStringX sender, ParametersValueChangedEventArgs e)
        {
            CenterPoint.TempMoveLock = Point1.TempMoveLock = true;
            if (Pad.MoveObject_DoMouseMove(this, PointD.Empty))
            {
                ComponentChanged(this);
            }
            else
            {

            }
            Pad.MoveObject_DoMouseUp(this);
            CenterPoint.TempMoveLock = Point1.TempMoveLock = false;
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("SectorCenter")]
        public PointBase CenterPoint
        {
            get { return InPoints[0]; }
            set { SetPoint(0, value); }
        }
        [MultilingualCategory("Structure"), MultilingualDisplayName("{0}1", "EndPoint")]
        public PointBase Point1
        {
            get { return InPoints[1]; }
            set { SetPoint(1, value); }
        }
        [MultilingualCategory("Parameter"), MultilingualDisplayName("{0}2", "EndPoint"), TypeConverter(typeof(IntelliPointConverter))]
        public PointD Point2
        {
            get { return Center.Offset(Start + Sweep, Radius); }
        }
        [Browsable(false)]
        public PointD NewPoint2
        {
            get { return NewCenter.Offset(NewStart + NewSweep, NewRadius); }
        }
        [MultilingualDisplayName("SectorCenter")]
        public override PointD Center
        {
            get
            {
                return base.Center;
            }
        }
        [MultilingualDisplayName(null, "SectorCenter", "Displayer")]
        public override PropertyDisplayer CenterDisplayer
        {
            get
            {
                return base.CenterDisplayer;
            }

            set
            {
                base.CenterDisplayer = value;
            }
        }
        public override PointD NewCenter
        {
            get
            {
                return CenterPoint.NewXY;
            }
        }
        public override PointD NewPCenter
        {
            get
            {
                return CenterPoint.NewPxy;
            }
        }
        public override double NewRadius
        {
            get
            {
                return NewCenter.DistanceTo(Point1.NewXY);
            }
        }
        public override bool CanDrawVirtual
        {
            get
            {
                return CenterPoint != null;
            }
        }
        public override double NewChordLength
        {
            get { return Point1.NewXY.DistanceTo(NewPoint2); }
        }

        public override double NewPStart
        {
            get
            {
                double tmp = NewPCenter.GetAngle(Point1.NewPxy);
                if (Pad.Coordinate != null)
                {
                    tmp += Pad.Coordinate.RotateAngle.RadValue;
                }
                return tmp;
            }
        }

        public override double NewStart
        {
            get { return NewCenter.GetAngle(Point1.NewXY); }
        }
        public override double NewPSweep
        {
            get
            {
                return getPSweep(NewStart, NewSweep);
            }
        }
        public override double NewSweep
        {
            get
            {
                return StrCentralAngle.RadValue;
            }
        }
        public override void SetPoint(int index, PointBase value)
        {
            base.SetPoint(index, value);
            if (value != null)
            {
                switch (index)
                {
                    case 0:
                        ske.Center = value.ToPointD();
                        pske.Center = value.Pxy;
                        break;
                }
            }
        }
        public override void Paint(Graphics gra)
        {
            if (ObjectState == ObjectState.Normal && Visible && IsCreationCompleted)
            {
                if (PSweep == 0)
                {
                    gra.DrawLineSegment(LinePen, CenterPoint.Pxy, Point1 == null ? SpareCoordinate : Point1.Pxy);
                }
                else
                {
                    double rot = Pad.Coordinate == null ? 0 : -Pad.Coordinate.RotateAngle.DegreeValue;
                    Brush bru = FillBrush;
                    if (bru != null)
                    {
                        if (Settings.Instance.SectorShowFullCircle && Math.Abs(StrCentralAngle.RadValue) > Core.Helper.Angle.RadRound)
                        {
                            gra.DrawCircle(LinePen, bru, PCenter, rot, pske.LongAxisHalfLength, pske.ShortAxisHalfLength);
                        }
                        gra.DrawSector(LinePen, bru, PCenter, rot, pske.LongAxisHalfLength, pske.ShortAxisHalfLength, PStart * Core.Helper.Angle.RToD, PSweep * Core.Helper.Angle.RToD);
                    }
                }
            }
            base.Paint(gra);
        }
        public override void PaintVirtualObjects(Graphics gra)
        {
            if (CanDrawVirtual)
            {
                if (PSweep == 0)
                {
                    gra.DrawLineSegment(CenterPoint.Pxy, Point1 == null ? SpareCoordinate : Point1.Pxy);
                }
                else
                {
                    double rot = Pad.Coordinate == null ? 0 : -Pad.Coordinate.RotateAngle.DegreeValue;
                    if (Settings.Instance.SectorShowFullCircle && Math.Abs(StrCentralAngle.RadValue) > Core.Helper.Angle.RadRound)
                    {
                        gra.DrawCircle(PCenter, rot, pske.LongAxisHalfLength, pske.ShortAxisHalfLength);
                    }
                    gra.DrawSector(PCenter, rot, pske.LongAxisHalfLength, pske.ShortAxisHalfLength, PStart * Core.Helper.Angle.RToD, PSweep * Core.Helper.Angle.RToD);
                }
            }
            base.PaintVirtualObjects(gra);
        }
        public override void PaintTrack(Graphics gra)
        {
            if (ShowTrack_)
            {
                for (int i = 0; i < track.Count; i++)
                {
                    gra.DrawSector(TrackPen, null, track[i].Key, track[i].Value[0], track[i].Value[1], track[i].Value[2], track[i].Value[3], track[i].Value[4]);
                }
            }
            base.PaintTrack(gra);
        }
        public override void ComponentBuilding(Base component)
        {
            if (CenterPoint == null) return;
            if (Flags.Shift)
            {
                SpareCoordinate = SpareCoordinate.GetProjectivePoint(CenterPoint.Pxy, Math.Tan(Utility.GetFixedAngle(
                        CenterPoint.Pxy.GetAngle(SpareCoordinate))));
            }
            PartialCircle pc = ske as PartialCircle;
            PartialEllipse pe = pske as PartialEllipse;
            PointD xy = Pad.PxyToXY(SpareCoordinate);
            Radius = CenterPoint.ToPointD().DistanceTo(xy);
            pc.Start = CenterPoint.ToPointD().GetAngle(xy);
            pc.Sweep = StrCentralAngle.RadValue;
            pe.Start = CenterPoint.Pxy.GetAngle(SpareCoordinate) + (Pad.Coordinate == null ? 0 : Pad.Coordinate.RotateAngle.RadValue);
            pe.Sweep = getPSweep(Start, Sweep);
            ArcLength = Radius * Sweep;
            ChordLength = xy.DistanceTo(Point2);
            Circumference = Radius * (Sweep + 2);
            Area = Math.PI * Radius * Radius * Sweep / Core.Helper.Angle.RadRound;
            Pad.BuildArcSamples(ske.Curves[0], pske.Curves[0], Center, Radius, Start, Sweep);
            ShowStatus($"{Resources.Translate("Creating")}{Resources.ColonSeparate}{Name}{Resources.LeftParenthesis}{Resources.Translate("ArcLength")} = {ArcLength}{Resources.CommaSeparate}{Resources.Translate("ChordLength")} = {ChordLength}{Resources.CommaSeparate}{Resources.Translate("Radius")} = {Radius}");
            base.ComponentBuilding(component);
        }
        double getPSweep(double start, double sweep)
        {
            double pend = Pad.RadToPRad(start + sweep), pstart = Pad.RadToPRad(start), res = pend - pstart;
            if (sweep > 0 && pend > pstart)
            {
                res -= Core.Helper.Angle.RadRound;
            }
            else if (sweep < 0 && pend < pstart)
            {
                res += Core.Helper.Angle.RadRound;
            }
            return res;
        }
        public override void ComponentChanged(ShapeBase component)
        {
            base.ComponentChanged(component);
            Circumference = Radius * (Sweep + 2);
            Area = Math.PI * Radius * Radius * Sweep / Core.Helper.Angle.RadRound;
        }
        public override void CreateComponents()
        {
            if (CenterPoint == null)
            {
                CenterPoint = Pad.CreatePoint(Resources.Translate("SectorCenter"), SpareCoordinate_);
                Pad.TrackObj = this;
            }
            else
            {
                Point1 = Pad.CreatePoint($"{Resources.Translate("EndPoint")}1", SpareCoordinate_, true, new PointCollectionBase[] { new Universal(CenterPoint.ToPointD()) });
                base.CreateComponents();
            }
        }
    }
}
